import sys
import threading
import dbus, gobject, avahi
from dbus import DBusException
import dbus.glib
#from dbus.mainloop.glib import DBusGMainLoop

from Bases import BaseObject

service_type_browsers = {}
service_browsers = {}

class AvahiDiscover(BaseObject):
    def __init__(self, **kwargs):
        super(AvahiDiscover, self).__init__(**kwargs)
        self.register_signal('new_host', 'remove_host')
        self.stype = kwargs.get('stype')
        self.id = kwargs.get('id', self.stype)
        self.domain = kwargs.get('domain')
        self.serv_info_map = {2:'name', 3:'stype', 4:'domain', 5:'hostname', 7:'address', 8:'port', 9:'text'}
        self.mainloop = None
        if 'daemon' in sys.argv:
            self.mainloop = dbus.mainloop.glib.DBusGMainLoop()
        self.bus = dbus.SystemBus(mainloop=self.mainloop)
        self.server = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER)
        if self.domain is None:
            # Explicitly browse .local
            self.browse_domain(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, "local")
                        
            # Browse for other browsable domains
            db = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME,
                                self.server.DomainBrowserNew(avahi.IF_UNSPEC,avahi.PROTO_UNSPEC,"",avahi.DOMAIN_BROWSER_BROWSE,dbus.UInt32(0))),
                                avahi.DBUS_INTERFACE_DOMAIN_BROWSER)
            db.connect_to_signal('ItemNew', self.new_domain)
        else:
            # Just browse the domain the user wants us to browse
            self.browse_domain(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, domain)
            
        if self.mainloop:
            gobject.threads_init()
            dbus.mainloop.glib.threads_init()
            self.gmainloop = gobject.MainLoop()
            self.run_thread = threading.Thread(target=self.gmainloop.run)
            self.run_thread.setDaemon(True)
            self.run_thread.start()
    
    def do_disconnect(self):
        if hasattr(self, 'gmainloop'):
            self.gmainloop.quit()
            
    def new_domain(self, interface, protocol, domain, flags):
        if domain != "local":
            self.browse_domain(interface, protocol, domain)
    
    def browse_domain(self, interface, protocol, domain):
        global service_type_browsers

        # Are we already browsing this domain?
        if service_type_browsers.has_key((interface, protocol, domain)):
            return

        if self.stype is None:
            print "Browsing domain '%s' on %i.%i ..." % (domain, interface, protocol)

            try:
                b = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, self.server.ServiceTypeBrowserNew(interface, protocol, domain, dbus.UInt32(0))),  avahi.DBUS_INTERFACE_SERVICE_TYPE_BROWSER)
            except DBusException, e:
                print e
                error_msg("You should check that the avahi daemon is running.\n\nError : %s" % e)
                sys.exit(0)
                
            b.connect_to_signal('ItemNew', self.new_service_type)

            service_type_browsers[(interface, protocol, domain)] = b
        else:
            self.new_service_type(interface, protocol, self.stype, domain, None)
    
    def new_service_type(self, interface, protocol, stype, domain, flags):
        global service_browsers

        # Are we already browsing this domain for this type? 
        if service_browsers.has_key((interface, protocol, stype, domain)):
            return
        
        print "Browsing for services of type '%s' in domain '%s' on %i.%i ..." % (stype, domain, interface, protocol)
        
        b = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, self.server.ServiceBrowserNew(interface, protocol, stype, domain, dbus.UInt32(0))),  avahi.DBUS_INTERFACE_SERVICE_BROWSER)
        b.connect_to_signal('ItemNew', self.new_service)
        b.connect_to_signal('ItemRemove', self.remove_service)

        service_browsers[(interface, protocol, stype, domain)] = b
    
    def new_service(self, interface, protocol, name, stype, domain, flags):
        #print "Found service '%s' of type '%s' in domain '%s' on %i.%i." % (name, stype, domain, interface, protocol)
        self.server.ResolveService(int(interface), int(protocol),
                                   name, stype, domain, avahi.PROTO_UNSPEC, dbus.UInt32(0),
                                   reply_handler=self.service_resolved, error_handler=self.print_error)

    def remove_service(self, *args):
        self.emit('remove_host', browser=self, data=self.parse_service_args(args))

    def service_resolved(self, *args):
        self.emit('new_host', browser=self, data=self.parse_service_args(args))

    def parse_service_args(self, arglist):
        serv_data = {}
        for x, arg in enumerate(arglist):
            if x in self.serv_info_map:
                key = self.serv_info_map[x]
                if key == 'text':
                    value = ''.join(avahi.txt_array_to_string_array(arglist[x]))[::-1]
                elif type(arglist[x]) == dbus.String:
                    value = str(arglist[x])
                elif type(arglist[x]) == dbus.UInt16:
                    value = int(arglist[x])
                serv_data.update({key:value})
        return serv_data
    
    def print_error(self, *args):
        print 'error_handler'
        print args[0]

if __name__ == '__main__':
    discoverer = AvahiDiscover('_xmlrpc._tcp')
    gobject.MainLoop().run()
